from MMU import MMU, memory
from files import stat
from data_type import bit_64
from decoder import type
from register_file import registers_file


class ecall:
    def __init__(self):
        self.a = [bit_64() for _ in range(8)]

    def update(self, rdM, wbM: bit_64, jobM: type, rdW, wbW: bit_64, jobW: type, rf: registers_file):
        for i in range(10, 18):
            self.a[i - 10] = rf.port_single_out(i)
        if jobM.wb and 10 <= rdM <= 17:
            self.a[rdM - 10] = wbM
        if jobW.wb and 10 <= rdW <= 17:
            self.a[rdW - 10] = wbW


code_end = 0
current_brk = 0x27000
stat_state = [0, 0]


def init(load_table: list):
    global code_end
    global current_brk
    end = load_table[0][1] + load_table[0][3]
    code_end = end


def fstat(fildes: int, buf, mem: memory, mmu: MMU):
    global stat_state
    now = stat[fildes][stat_state[fildes]]
    stat_state[fildes] += 1
    for offset in range(144):
        vir_addr = buf + offset
        phy_addr = mmu.to_phy_addr(vir_addr, 2, mem)
        mem.data_8_port_in(now[offset], phy_addr)
    return 0


def brk(addr):
    global current_brk
    if addr >= code_end:
        current_brk = addr
    return current_brk


def lseek(fildes: int, offset: int, whence: int):
    if fildes == 0 or fildes == 1:
        return -29


def read(fildes: int, buf, count, mem: memory, mmu: MMU):
    if fildes == 0:
        broad = input().encode()
        broad += b'\0'
        size = min(count, len(broad))
        for offset in range(size):
            vir_addr = buf + offset
            phy_addr = mmu.to_phy_addr(vir_addr, 1, mem)
            mem.data_8_port_in(broad[offset], phy_addr)
        return size


def write(fildes: int, buf, count, mem: memory, mmu: MMU):
    if fildes == 1:
        out = ""
        cnt = 0
        for offset in range(count):
            vir_addr = buf + offset
            phy_addr = mmu.to_phy_addr(vir_addr, 1, mem)
            byte = mem.data_8_port_out(phy_addr)
            x = byte.to_bytes(1, "little").decode()
            out += x
            cnt += 1
        print(out, end='')
        return cnt


def close(fildes: int):
    if fildes == 0 or fildes == 1 or fildes == 2:
        return 0


def sys_lseek(regs: ecall, mem: memory, mmu: MMU):
    return lseek(regs.a[0].val, regs.a[1].val, regs.a[2].val)


def sys_brk(regs: ecall, mem: memory, mmu: MMU):
    return brk(regs.a[0].val)


def sys_close(regs: ecall, mem: memory, mmu: MMU):
    return close(regs.a[0].val)


def sys_fstat(regs: ecall, mem: memory, mmu: MMU):
    return fstat(regs.a[0].val, regs.a[1].val, mem, mmu)


def sys_read(regs: ecall, mem: memory, mmu: MMU):
    return read(regs.a[0].val, regs.a[1].val, regs.a[2].val, mem, mmu)


def sys_write(regs: ecall, mem: memory, mmu: MMU):
    return write(regs.a[0].val, regs.a[1].val, regs.a[2].val, mem, mmu)


def sys_exit(regs: ecall, mem: memory, mmu: MMU):
    exit()


sys_call_table = {57: sys_close, 62: sys_lseek, 63: sys_read, 64: sys_write, 80: sys_fstat, 93: sys_exit, 214: sys_brk}


def sys_call(regs: ecall, mem: memory, mmu: MMU):
    return sys_call_table[regs.a[7].val](regs, mem, mmu)
